package com.cheesmo.nzb.client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.Properties;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;

public class ClientConfig
{
	Preferences prefs = null;
	Properties cfg = null;

	public ClientConfig()
	{
		this.cfg = new Properties();
		try {
			this.cfg.load(ClassLoader.getSystemResourceAsStream("com/cheesmo/nzb/client/preferences.cfg"));
		} catch (IOException e1) {
			e1.printStackTrace();
		}

		this.prefs = Preferences.userNodeForPackage(getClass());

		if (!this.prefs.getBoolean("init", false))
			try {
				initPrefs();
				display();
				this.prefs.putBoolean("init", true);
			} catch (Exception e) {
				System.err.println("Error setting preferences.");
				e.printStackTrace();
			}
	}

	public void editPrefs()
	{
		display();
		System.out.println("Select the option to modify [1-" + (getNumOfPrefs() + 1) + "]\n");
		int chosen = readOption();
		while (chosen != getNumOfPrefs() + 1)
		{
			if ((chosen < 1) || (chosen > getNumOfPrefs())) {
				System.out.println("Select the option to modify [1-" + (getNumOfPrefs() + 1) + "]\n");
				chosen = readOption();
			}
			else {
				String id = this.cfg.getProperty("pref.id." + chosen);
				String display = this.cfg.getProperty("pref.display." + chosen);
				String type = this.cfg.getProperty("pref.type." + chosen);
				String def = this.cfg.getProperty("pref.default." + chosen);
				initPref(id, display, type, def);
				display();
				chosen = readOption();
			}
		}
	}

	private int readOption() {
		BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

		int intValue = -1;
		try {
			String value = reader.readLine();
			intValue = Integer.parseInt(value);
		} catch (Throwable localThrowable)
		{
		}

		return intValue;
	}

	private void initPrefs()
	{
		System.out.println("This is your first time running nzb client.");
		for (int prefNum = 1;; prefNum++) {
			String id = this.cfg.getProperty("pref.id." + prefNum);
			String display = this.cfg.getProperty("pref.display." + prefNum);
			String type = this.cfg.getProperty("pref.type." + prefNum);
			String def = this.cfg.getProperty("pref.default." + prefNum);
			if (id == null) {
				break;
			}
			initPref(id, display, type, def);
		}
	}

	private void initPref(String id, String display, String type, String def)
	{
		if (type.equals("string"))
			getStringPref(display, id, this.prefs.get(id, def), false);
		else if (type.equals("int"))
			getIntegerPref(display, id, this.prefs.get(id, def));
		else if (type.equals("bool"))
			getBooleanPref(display, id, this.prefs.get(id, def));
		else if (type.equals("password"))
			getStringPref(display, id, null, true);
	}

	private String getStringPref(String string, String prefId, String def, boolean isPassword)
	{
		System.out.println(string + (def != null ? " [" + def + "]:" : ":\n"));
		BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
		String value = "";
		try {
			if ((isPassword) && (isPasswordSupported()))
			{
				value = new String(System.console().readPassword());
			}
			else
			{
				value = reader.readLine();
			}
			if (("".equals(value.trim())) && (def != null))
				value = def;
			this.prefs.put(prefId, value);
			return value;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	private boolean isPasswordSupported()
	{
		try
		{
			Class.forName("java.io.Console");
			return true;
		} catch (ClassNotFoundException e) {
		}
		return false;
	}

	private int getIntegerPref(String string, String prefId, String def)
	{
		String value = getStringPref(string, prefId, def, false);
		while (!isValidInt(value)) {
			this.prefs.remove(prefId);
			System.out.println("Valid integer required.");
			value = getStringPref(string, prefId, def, false);
		}
		this.prefs.putInt(prefId, Integer.parseInt(value));
		return Integer.parseInt(value);
	}

	private boolean getBooleanPref(String string, String prefId, String def)
	{
		String value = getStringPref(string, prefId, def, false);
		while (!isValidBool(value)) {
			this.prefs.remove(prefId);
			System.out.println("Valid boolean required.");
			value = getStringPref(string, prefId, def, false);
		}
		this.prefs.putBoolean(prefId, Boolean.parseBoolean(value));
		return Boolean.parseBoolean(value);
	}

	private boolean isValidInt(String value)
	{
		try
		{
			Integer.parseInt(value);
			return true;
		} catch (NumberFormatException ex) {
		}
		return false;
	}

	private boolean isValidBool(String value)
	{
		try {
			Boolean.parseBoolean(value);
			return true;
		} catch (NumberFormatException ex) {
		}
		return false;
	}

	public String getServer()
	{
		return this.prefs.get("server.name", null);
	}

	public String getUsername() {
		return this.prefs.get("server.username", null);
	}

	public String getPassword()
	{
		return this.prefs.get("server.password", null);
	}

	public int getPort() {
		return this.prefs.getInt("server.port", 23);
	}

	public boolean requiresAuthentication() {
		return this.prefs.getBoolean("server.auth", true);
	}

	public int getMaxConnections() {
		return this.prefs.getInt("server.connections", 2);
	}

	public String getDownloadDir() {
		return getPropertyBasedPref("dir.download");
	}

	public String getCacheDir() {
		return getPropertyBasedPref("dir.cache");
	}

	private String getPropertyBasedPref(String key)
	{
		String def = null;
		for (int i = 1; i < getNumOfPrefs() + 1; i++) {
			if (key.equals(this.cfg.getProperty("pref.id." + i))) {
				def = this.cfg.getProperty("pref.default." + i);
			}
		}
		String pref = this.prefs.get(key, def);

		if (pref.indexOf("$") == -1) {
			return pref;
		}
		for (Iterator<Object> i = System.getProperties().keySet().iterator(); i.hasNext();) {
			String propName = (String) i.next();
			if (pref.indexOf("${" + propName + "}") > -1) {
				pref = pref.replace("${" + propName + "}", System.getProperty(propName));
			}
		}
		return pref;
	}

	public void display() {
		System.out.println("[Preferences]");
		int displayLength = 16;
		for (int prefNum = 1;; prefNum++) {
			String id = this.cfg.getProperty("pref.id." + prefNum);
			String display = this.cfg.getProperty("pref.display." + prefNum);
			String type = this.cfg.getProperty("pref.type." + prefNum);
			String def = this.cfg.getProperty("pref.default." + prefNum);

			if (id == null)
			{
				break;
			}
			if (type.equals("password"))
				System.out.println("[" + prefNum + "] " + pad(display, displayLength) + ": " + mask(this.prefs.get(id, def)));
			else {
				System.out.println("[" + prefNum + "] " + pad(display, displayLength) + ": " + this.prefs.get(id, def));
			}

		}

		System.out.println("[" + (getNumOfPrefs() + 1) + "] Quit");
	}

	private int getNumOfPrefs()
	{
		int prefNum = 1;
		while (true)
		{
			String id = this.cfg.getProperty("pref.id." + prefNum);
			if (id == null) {
				break;
			}
			prefNum++;
		}
		return prefNum - 1;
	}

	private String pad(String display, int displayLength)
	{
		while (display.length() < displayLength) {
			display = " " + display;
		}
		return display;
	}

	private String mask(String value) {
		StringBuffer toReturn = new StringBuffer();
		for (int i = 0; i < value.length(); i++) {
			toReturn.append('*');
		}
		return toReturn.toString();
	}

	public void reset() {
		try {
			this.prefs.removeNode();
			System.out.println("Preferences have been reset.");
		} catch (BackingStoreException e) {
			e.printStackTrace();
		}
	}
}
